#pragma once

#include"meta_stream.hpp"

using namespace meta_ios;
using namespace exp_utilities;
using literal_types::no_exist_type;

using protocols::stream_to_t;


struct to_any {
    template<class T>
    operator T();
};

template<class ...Tys>
struct construct_list : exp_list<Tys...> {
    template<class T>
    struct apply {
        static constexpr bool value = std::is_constructible_v<T, Tys...>;
    };
};

template<class T>
struct test_constructible {
    template<class this_list>
    struct apply {
        static constexpr bool value = !meta_invoke<this_list, T>::value;
    };
};

template<class T>
constexpr auto count_member = transfer_until<
    meta_ostream<construct_list<>>,
    meta_repeat_istream<to_any>,
    protocols::only_stream_to_unref<test_constructible<T>>
>::to::type::length - 1;


namespace construct_deduction {

    template<class T>
    constexpr std::size_t size() {
        return count_member<T>;
    }
    template<class T> requires std::is_aggregate_v<T>
    consteval auto construction_type_deduction(T obj) {
        if constexpr (size<T>() == 1) {
            auto [a] = obj;
            return exp_list<decltype(a)>{};
        }
        else if constexpr (size<T>() == 2) {
            auto [a, b] = obj;
            return exp_list<decltype(a), decltype(b)>{};
        }
        else if constexpr (size<T>() == 3) {
            auto [a, b, c] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c)>{};
        }
        else if constexpr (size<T>() == 4) {
            auto [a, b, c, d] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d)>{};
        }
        else if constexpr (size<T>() == 5) {
            auto [a, b, c, d, e] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e)>{};
        }
        else if constexpr (size<T>() == 6) {
            auto [a, b, c, d, e, f] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f)>{};
        }
        else if constexpr (size<T>() == 7) {
            auto [a, b, c, d, e, f, g] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g)>{};
        }
        else if constexpr (size<T>() == 8) {
            auto [a, b, c, d, e, f, g, h] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h)>{};
        }
        else if constexpr (size<T>() == 9) {
            auto [a, b, c, d, e, f, g, h, i] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i)>{};
        }
        else if constexpr (size<T>() == 10) {
            auto [a, b, c, d, e, f, g, h, i, j] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i), decltype(j)>{};
        }
        else if constexpr (size<T>() == 11) {
            auto [a, b, c, d, e, f, g, h, i, j, k] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i), decltype(j), decltype(k)>{};
        }
        else if constexpr (size<T>() == 12) {
            auto [a, b, c, d, e, f, g, h, i, j, k, l] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i), decltype(j), decltype(k), decltype(l)>{};
        }
        else if constexpr (size<T>() == 13) {
            auto [a, b, c, d, e, f, g, h, i, j, k, l, m] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i), decltype(j), decltype(k), decltype(l), decltype(m)>{};
        }
        else if constexpr (size<T>() == 14) {
            auto [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i), decltype(j), decltype(k), decltype(l), decltype(m), decltype(n)>{};
        }
        else if constexpr (size<T>() == 15) {
            auto [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = obj;
            return exp_list<decltype(a), decltype(b), decltype(c), decltype(d), decltype(e), decltype(f), decltype(g), decltype(h), decltype(i), decltype(j), decltype(k), decltype(l), decltype(m), decltype(n), decltype(o)>{};
        }
        else return no_exist_type{};
    }
}

template<class T>
constexpr auto deduct_constructor(T obj) {
    return construct_deduction::construction_type_deduction(T{});
}